iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
Modern Web

Golang x Echo 30 天:零基礎GO , 後端入門系列 第 17

以 Go + Echo 打造部落格|第 1 篇:專案初始化與骨架(MVP)

  • 分享至 

  • xImage
  •  

以 Go + Echo 打造部落格|第 1 篇:專案初始化與骨架(MVP)

以 Go + Echo 打造部落格|第 1 篇:專案初始化與骨架(MVP)

日期:2025-10-01(Asia/Taipei)

今天先把地基打好:把專案跑起來、路由打通、目錄整齊、能一鍵啟動。完成後你會有一個最小可行版(MVP)的部落格伺服器:首頁回 HTML、健康檢查回 JSON,之後功能就像樂高慢慢疊上去~😄
小辭典
Echo:Go 的熱門 Web 框架,用來處理 HTTP 請求/回應。
MVP:Minimum Viable Product,能跑、可用、最小必需。
.env:環境變數檔,把「會因環境不同而改的設定」獨立放。


本篇要做到什麼?

  • go mod init 建專案、裝 Echo v4。
  • 建一個乾淨的目錄結構(以後加功能不會亂)。
  • 開兩條路由:
    • GET /:回極簡 HTML(Hello Blog)。
    • GET /health:回 JSON(健康檢查)。
  • .env 控制埠號,make run 一鍵啟動 🚀

步驟清單(骨架 → 填充)

我們先把骨架搭起來,再把程式補滿。每一步都能單獨跑,卡住就回來對表。

1) 建專案、安裝套件

mkdir go-echo-blog && cd go-echo-blog
go mod init example.com/go-echo-blog
go get github.com/labstack/echo/v4
go get github.com/joho/godotenv

godotenv:啟動時自動讀 .env,本機開發更輕鬆。


2) 建目錄(先把抽屜分好)

mkdir -p cmd/server
mkdir -p internal/http/handlers
mkdir -p internal/core/domain
mkdir -p internal/core/services
mkdir -p internal/storage/postgres
mkdir -p web/templates
mkdir -p web/static
mkdir -p migrations

先整理一下比較好,這樣等下要放東西才不會打結。


3) 準備 .envMakefile

.env.example

cat > .env.example << 'EOF'
APP_ENV=development
PORT=1323
SITE_NAME=My Echo Blog
EOF

複製一份正式檔:

cp .env.example .env

Makefile

cat > Makefile << 'EOF'
APP_NAME=go-echo-blog

.PHONY: run dev tidy fmt clean

run:
	go run ./cmd/server

dev:
	go run ./cmd/server

tidy:
	go mod tidy

fmt:
	gofmt -w .

clean:
	rm -f $(APP_NAME)
EOF

4) 放首頁模板(先素顏,之後再加樣式)

web/templates/index.html

<!doctype html>
<html lang="zh-Hant">
<head>
  <meta charset="utf-8">
  <title>{{.Title}}</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- 第 3 篇再接 Tailwind;今天先清爽就好 -->
  <style>
    body { font-family: system-ui, -apple-system, "Noto Sans TC", Arial; margin: 40px; }
    .badge { display:inline-block; padding:4px 8px; border-radius:6px; background:#eef; }
  </style>
</head>
<body>
  <h1>👋 {{.Title}}</h1>
  <p class="badge">MVP ready • {{.Now}}</p>
  <p>歡迎來到你的 Go + Echo 部落格!今天先有首頁與健康檢查。</p>
  <ul>
    <li><a href="/health">/health</a>(JSON 健康檢查)</li>
  </ul>
</body>
</html>

5) 寫處理器與主程式(把靈魂裝進去)

internal/http/handlers/home.go

package handlers

import (
	"net/http"
	"time"

	"github.com/labstack/echo/v4"
)

// HomeHandler:回首頁 HTML
func HomeHandler(c echo.Context) error {
	data := map[string]any{
		"Title": "Hello Blog",
		"Now":   time.Now().In(time.FixedZone("Asia/Taipei", 8*60*60)).Format("2006-01-02 15:04:05"),
	}
	return c.Render(http.StatusOK, "index.html", data)
}

// HealthHandler:回健康檢查 JSON
func HealthHandler(c echo.Context) error {
	return c.JSON(http.StatusOK, map[string]any{
		"ok":       true,
		"app":      "go-echo-blog",
		"datetime": time.Now().In(time.FixedZone("Asia/Taipei", 8*60*60)).Format(time.RFC3339),
	})
}

cmd/server/main.go

package main

import (
	"html/template"
	"io"
	"log"
	"net/http"
	"os"

	"github.com/joho/godotenv"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"

	"example.com/go-echo-blog/internal/http/handlers"
)

// TemplateRenderer 把 html/template 接到 Echo
type TemplateRenderer struct {
	t *template.Template
}

func (tr *TemplateRenderer) Render(w io.Writer, name string, data any, c echo.Context) error {
	return tr.t.ExecuteTemplate(w, name, data)
}

func mustGetEnv(key, def string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return def
}

func main() {
	_ = godotenv.Load() // 本機讀 .env;雲端可不需要

	port := mustGetEnv("PORT", "1323")

	e := echo.New()
	e.Use(middleware.Recover())
	e.Use(middleware.Logger())
	e.Use(middleware.CORS())

	// 靜態資源(目前空的)
	e.Static("/static", "web/static")

	// 模板
	t := template.Must(template.ParseGlob("web/templates/*.html"))
	e.Renderer = &TemplateRenderer{t: t}

	// 路由
	e.GET("/", handlers.HomeHandler)
	e.GET("/health", handlers.HealthHandler)
	e.GET("/_ping", func(c echo.Context) error { return c.String(http.StatusOK, "pong") })

	log.Printf("Server on :%s 🚦", port)
	if err := e.Start(":" + port); err != nil && err != http.ErrServerClosed {
		log.Fatal(err)
	}
}

檔案樹(照這樣放就行)

go-echo-blog/
├─ cmd/
│  └─ server/
│     └─ main.go
├─ internal/
│  ├─ core/
│  │  ├─ domain/
│  │  └─ services/
│  ├─ http/
│  │  └─ handlers/
│  │     └─ home.go
│  └─ storage/
│     └─ postgres/
├─ migrations/
├─ web/
│  ├─ static/
│  └─ templates/
│     └─ index.html
├─ .env
├─ .env.example
├─ Makefile
└─ go.mod

go.mod(建立後自動產生,示意):

module example.com/go-echo-blog

go 1.22.0

require (
	github.com/joho/godotenv v1.5.1
	github.com/labstack/echo/v4 v4.11.4
)

資料庫變更

本篇沒有動資料庫(輕鬆開局 ✨)。第 2 篇再用 Docker 起 Postgres、pgxpool 連線、goose 建五張表:userspoststagspost_tagssessions


測試區(curl)

啟動:

make run
# 看到 "Server on :1323" 就成功

首頁(HTML):

curl -i http://localhost:1323/

健康檢查(JSON):

curl -s http://localhost:1323/health | jq .

存活探針(純文字):

curl -s http://localhost:1323/_ping
# 會回:pong

常見坑(配解法)

  1. 1323 埠被占用:改 .envPORT,或先把舊程式關掉。
  2. 找不到模板:請在專案根目錄跑 make run,而且 web/templates/index.html 路徑別打錯。
  3. 模板語法錯:先確認只用 {{.Title}}{{.Now}},不要多一個或少一個大括號。
  4. .env 沒生效:確認有 cp .env.example .env,或直接在環境變數設定 PORT
  5. module 路徑不一致go.mod 的 module 名要跟 import "example.com/go-echo-blog/..." 對上;用 GitHub 的話請換成自己的 repo 路徑。

小結 & 下一篇

今天把「骨架」立好了:首頁、健康檢查、環境變數、一鍵啟動都 OK ✅
下一篇(第 2 篇):Docker 啟 Postgres、pgxpool 連線、goose 建表。
小作業(加分)

  • 新增 GET /about,回一段 HTML,內容放站名與今天日期。
  • 把首頁的時間顯示改成「台北時間」好讀版(例如 2025-10-01 10:30:00)。

附註

為了好維護,提交前可以跑:

make tidy
make fmt

上一篇
用 Go + Echo 打造你的第一個 TodoList , 第 6 篇:把資料搬進資料庫—Postgres 串接
下一篇
以 Go + Echo 打造部落格|第 2 篇 Postgres + pgxpool + goose
系列文
Golang x Echo 30 天:零基礎GO , 後端入門24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言